Última atualização: 29/04/2022

O fenômeno da piscada

A piscada pode ser considerada como um erro de registro em diversas variáveis, como na movimentação ocular, fixação e diâmetro pupilar. Este erro tem um formato característico, semelhante a um pico destoante da mediana do sinal no qual há falta de dados. Somente a variável referente ao aspecto pupilar não registraria a piscada como um erro, uma vez que o fenômeno da piscada implica necessáriamente numa mudança gradativa da razão do aspecto pupilar (aspect ratio).

Essa mudança corresponde a mudança da proporção (aspect ratio) da identificação da pupila como circulo perfeito. Durante uma piscada, as pálpebras ocludem a pupila e portanto a pupila deixa de estar representada como um círculo, passando a um formato elíptico, chegando a nenhuma identificação (oclusão total) e voltando gradativamente ao formato circular novamente. Esse evento é descrito nas páginas 54 e 58 do manual do Eye-Tracker ViewPoint, onde diz respectivamente:

Blinks can be detected by monitoring the pupil aspect ratio. This is a dimensionless value, where 1.0 indicates a perfect circle.

10.2.4 Blinks

As the eye lid comes down during a blink, the elliptical fit to the pupil becomes increasingly flat before it disappears. This characteristic change in the aspect ratio of elliptical fit to the pupil can be used to detect blinks. A blink is classified as the pupil aspect ratio crossing below the threshold.

Este fenômeno também é explorado por Youngjun Cho, no artigo Rethinking Eye-blink: Assessing Task Difficulty through Physiological Representation of Spontaneous Blinking no qual é proposto a identificação de piscadas através da análise da série temporal do aspecto pupilar.

Neste artigo, a abordagem utilizada não estipula um limiar (threshold) para determinar quais pontos são piscadas e quais não são, buscando uma análise de frequência de toda série temporal. A análise é realizada filtrando o sinal (média móvel de 1 segundo e filtro passa-banda na frequência de 0.033Hz a 0.4167Hz). Estes valores foram escolhidos visando uma frequência de piscada de 2 a 25 por minuto. Posteriormente, foi criado um espectrograma calculando a potência de Lomb-Scargle e empregado um algortimo de aprendizado de máquina a fim de classificar os períodos de maior e menor frequência de piscadas.

Hipóteses

  • Aspecto pupilar - PupilAspect - deve estar igual ou próximo a 1 durante momentos de “não piscada”. Portanto, boa parte da distribuição dos dados deve ter mediana próximo de 1.

  • Um evento de piscada consiste na mudança do Aspecto pupilar de 1 para 0. Portanto, devem exister pontos destoantes, próximos a zero, com uma certa frequência.

  • Essa mudança é gradativa, sendo possível notar o onset e o offset da piscada, tal como um vale. Logo, nem todos os pontos destoantes são piscadas, mas sim “parte” da piscada.

  • Demais variáveis devem apresentar perturbações, desviando bruscamente o registro da mediana do sinal –> Registros fora de mediana poderiam ser considerados piscadas ou ruídos (outliers).

Visualização dos dados

PupilAspect x TotalTime

Plotagem da série temporal da variável PupilAspect de todos os participantes do grupo controle ao longo do tempo percorrido. Foi considerado um gráfico de linha, uma vez que o dado é contínuo, sendo aplicado uma função de suavização de tendência. A qualidade do dado é classificada em uma escala de 0 a 5, sendo quanto menor o valor melhor.

pupAspXtime_all = vector(mode="list")

i = 1
for (participante in participantes) {
  pupAspXtime_all[[i]] <- ggplot(data = participante) +
    geom_path(mapping = aes(x = TotalTime, y = PupilAspect, colour = Quality)) +
    geom_smooth(mapping = aes(x = TotalTime, y = PupilAspect)) +
    ggtitle(names(participantes[i]))
  
  i <- i+1
}

pupAspXtime_all
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

## 
## [[12]]

## 
## [[13]]

## 
## [[14]]

## 
## [[15]]

## 
## [[16]]

## 
## [[17]]

## 
## [[18]]

## 
## [[19]]

## 
## [[20]]

## 
## [[21]]

## 
## [[22]]

## 
## [[23]]

## 
## [[24]]

## 
## [[25]]

## 
## [[26]]

Distribuição do PupilAspect

Plotagem do histograma da variável PupilAspect de todos os participantes. Marcação da mediana em azul, Q1 e Q3 em amarelo e limite máximo e mínimo do intervalo interquartil em vermelho.

O limite máximo e mínimo equivalem a 1,5 vezes o intervalo interquartil (IQR).

pupAspHist = vector(mode = "list")  
  
i = 1
for (participante in participantes) {
  pupAspHist[[i]] <- ggplot(data = participante) +
    geom_histogram(mapping = aes(PupilAspect), binwidth = 0.005) +
    geom_vline(xintercept = median(participante$PupilAspect), linetype="dotted", color = "blue", size = 1) +
    geom_vline(xintercept = quantile(participante$PupilAspect, 1/4), linetype="dotted", color = "yellow", size = 1) +
    geom_vline(xintercept = quantile(participante$PupilAspect, 3/4), linetype="dotted", color = "yellow", size = 1) +
    geom_vline(xintercept = quantile(participante$PupilAspect, 1/4) - 1.5*IQR(participante$PupilAspect), linetype="dotted", color = "red", size = 1) +
    geom_vline(xintercept = quantile(participante$PupilAspect, 3/4) + 1.5*IQR(participante$PupilAspect), linetype="dotted", color = "red", size = 1) +
    ggtitle(names(participantes[i]))
  i <- i+1
}
 
pupAspHist
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

## 
## [[12]]

## 
## [[13]]

## 
## [[14]]

## 
## [[15]]

## 
## [[16]]

## 
## [[17]]

## 
## [[18]]

## 
## [[19]]

## 
## [[20]]

## 
## [[21]]

## 
## [[22]]

## 
## [[23]]

## 
## [[24]]

## 
## [[25]]

## 
## [[26]]

Detecção de piscada

1. Tentativa de Detecção por Intervalo Interquartil

Objetivo: Classificar como “piscada” todos os pontos que estão além dos limites do intervalo interquartil.

Para esta tentativa serão considerados somente os limites inferiores, uma vez que a natureza do evento reflete somente a diminuição do PupilAspect para valores inferiores a 1 e, por consequência, inferiores a massa da distribuição. A massa da distribuição reflete qual aspecto pupilar o participante apresenta na maior parte do tempo.

Teste em participante “ideal”

Foi escolhido o participante 2179, por ter um baixo intervalo interquartil e distribuição relativamente simétrica.

limite_inferior = quantile(participantes$`2179`$PupilAspect, 1/4) - 1.5*IQR(participantes$`2179`$PupilAspect)

out_points = participantes$`2179`$PupilAspect < limite_inferior

paste("Total de valor abaixo do limite inferior: ", 
      sum(out_points == TRUE))
## [1] "Total de valor abaixo do limite inferior:  831"
pupAspectXTime_out_graph <- ggplot(data = participantes$`2179`) +
    geom_path(mapping = aes(x = TotalTime, y = PupilAspect, colour = out_points)) +
    scale_color_manual(values = c("black", "red")) +
    geom_hline(yintercept = limite_inferior, linetype="dotted")+
    ggtitle("2179")
    
pupAspectXTime_out_graph

Como resultado, 831 pontos foram classificados além do limite inferior sendo ou não potenciais piscadas. Considerando uma amostra de 450 segundos (7:30 minutos) onde todos estes pontos são piscadas, teríamos uma frequência de aproximadamente 110 piscadas por minuto. A critério de comparação, de acordo com a literatura, um indivíduo sem privação de sono apresenta de 9 a 13 piscadas por minuto, enquanto um individuo com privação de sono apresenta de 20 a 30 piscadas por minuto.

Existem algumas questões a serem avaliadas:

  • Nem todos os pontos destoantes são piscadas. Vários são pontos intermediários entre a oclusão total e a reabertura. Cabe entender como agregar esses pontos e contabilizá-los como uma piscada.

  • Há a possibilidade de que a mediana varie localmente e, portanto, varie o limiar de classificação. Para isso, podemos comparar recortes locais com o cálculo global de mediana e o cálculo local. Dependendo dos resultados, talvez adotar uma abordagem de classificação por mediana móvel possa ser uma opção.

Recorte de 1 minuto, utilizando cálculo global.

Recorte temporal de 1 minuto, utilizando o cálculo global da mediana e interquartil (para toda amostra)

pupAspectXTime_out_graph <- ggplot(data =
    participantes$`2179`[participantes$`2179`$TotalTime < 60, ]) +
    geom_path(
      mapping = aes(x = TotalTime, y = PupilAspect, colour = out_points[1:3556])) +
    scale_color_manual(values = c("black", "red")) +
    geom_hline(yintercept = limite_inferior, linetype="dotted")+
    ggtitle("2179")

paste("Total de valor abaixo do limite inferior: ", 
      sum(out_points[1:3556] == TRUE))
## [1] "Total de valor abaixo do limite inferior:  182"
pupAspectXTime_out_graph

Como resultado, 182 pontos foram identificados abaixo do limite inferior. Caso todos estes pontos sejam de fato “piscadas” teremos 182 piscadas por minuto, bem distante do observado na literatura.

Recorte de 1 minuto, utilizando cálculo local

Neste teste, a mediana e os intervalos interquartis foram calculados considerando somente os dados do recorte local.

participante_recorte = participantes$`2179`[participantes$`2179`$TotalTime < 60, ]

limite_inferior = quantile(participante_recorte$PupilAspect, 1/4) - 1.5*IQR(participante_recorte$PupilAspect)

out_points = participante_recorte$PupilAspect < limite_inferior

paste("Total de valor abaixo do limite inferior: ", 
      sum(out_points == TRUE))
## [1] "Total de valor abaixo do limite inferior:  160"
pupAspectXTime_out_graph <- ggplot(data = participante_recorte) +
    geom_path(mapping = aes(x = TotalTime, y = PupilAspect, colour = out_points)) +
    scale_color_manual(values = c("black", "red")) +
    geom_hline(yintercept = limite_inferior, linetype="dotted")+
    ggtitle("2179")
    
pupAspectXTime_out_graph

Como resultado, foram identificados 160 pontos. Mesmo sendo um valor mais próximo da literatura, continua sendo bem distoante.

Identificação de onduleta

pupAspectXTime_out_graph <- ggplot(data =
    participantes$`2179`[participantes$`2179`$TotalTime < 2, ]) +
    geom_path(
      mapping = aes(x = TotalTime, y = PupilAspect, colour = out_points[1:119])) +
    scale_color_manual(values = c("black", "red")) +
    geom_hline(yintercept = limite_inferior, linetype="dotted")+
    ggtitle("2179")

paste("Total de valor abaixo do limite inferior: ", 
      sum(out_points[1:119] == TRUE))
## [1] "Total de valor abaixo do limite inferior:  10"
pupAspectXTime_out_graph

Tentativa de deteção por algoritmo de I-DT modificado

Objetivo: Adaptar algoritmo de deteção de fixações por dispersão temporal em um algoritmo de deteção de piscadas.

Para essa tentativa serão realizadas as seguintes adaptações no algoritmo de I-DT prente em Salvucci and Goldberg (2000)

Salvucci, Dario D., and Joseph H. Goldberg. 2000. “The Symposium.” In, 71–78. Palm Beach Gardens, Florida, United States: ACM Press. https://doi.org/10.1145/355017.355028.